﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.OleDb;
using System.IO;
using System.Linq;
using TestOnlineProject.Common;
using TestOnlineProject.Models.Entities;
using TestOnlineProject.Models.DataAccess;
using System.Web;

namespace TestOnlineProject.Models.BusinessLogic
{
    public class ParticipantBLO
    {
        ParticipantDAO participantDAO = new ParticipantDAO();
        UserDAO userDAO = new UserDAO();
        SubjectDAO subjectDAO = new SubjectDAO();

        /// <summary>
        /// Select participant list.
        /// </summary>
        /// <param name="page">Number of page</param>
        /// <param name="subjectId">Subject id</param>
        /// <param name="keyWord">Key word</param>
        /// <param name="column">Column name</param>
        /// <param name="sort">Sort type</param>
        /// <returns>Participant list</returns>

        public List<Participant> ToList(ref List<NoModel> noList, int? page, int subjectId = 0, string keyWord = "", string column = "", string sort = "")
        {
            try
            {
                // Participant list.
                var participants = participantDAO.SelectBySubjectId(subjectId);

                // Search by key word.
                if (keyWord.Equals(""))
                {
                    participants = participants.Where(p => p.SubjectId == subjectId).OrderBy(p => p.UserId).ToList();
                }
                else
                {
                    participants = participants.Where(p => (p.SubjectId == subjectId && (p.User.Username.ToLower().Contains(keyWord.ToLower())
                        || p.User.FullName.ToLower().Contains(keyWord.ToLower())))).OrderBy(u => u.UserId).ToList();
                }

                // Sort by column
                switch (column + sort)
                {
                    case "NoAsc":
                        participants = participants.Where(p => p.SubjectId == subjectId).OrderBy(p => p.UserId).ToList();
                        break;
                    case "NoDesc":
                        participants = participants.Where(p => p.SubjectId == subjectId).OrderByDescending(p => p.UserId).ToList();
                        break;
                    case "FullNameAsc":
                        participants = participants.Where(p => p.SubjectId == subjectId).OrderBy(p => p.User.FullName).ToList();
                        break;
                    case "FullNameDesc":
                        participants = participants.Where(p => p.SubjectId == subjectId).OrderByDescending(p => p.User.FullName).ToList();
                        break;
                    case "JoinedTimeAsc":
                        participants = participants.Where(p => p.SubjectId == subjectId).OrderBy(p => p.JoinedTime).ToList();
                        break;
                    case "JoinedTimeDesc":
                        participants = participants.Where(p => p.SubjectId == subjectId).OrderByDescending(p => p.JoinedTime).ToList();
                        break;
                    case "IsActivatedAsc":
                        participants = participants.Where(p => p.SubjectId == subjectId).OrderBy(p => p.IsActivated).ToList();
                        break;
                    case "IsActivatedDesc":
                        participants = participants.Where(p => p.SubjectId == subjectId).OrderByDescending(p => p.IsActivated).ToList();
                        break;
                    default: break;
                }

                // Generate no. List.
                foreach (var u in participants)
                {
                    noList.Add(new NoModel() { Id = u.UserId });
                }
                noList = noList.OrderBy(n => n.Id).ToList();
                for (int i = 0; i < noList.Count; i++)
                {
                    noList[i].No = i + 1;
                }

                return participants;
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// Add new participant.
        /// </summary>
        /// <param name="participant">Participant object</param>
        /// <returns>Number of participant has been added</returns>

        public int Add(Participant participant)
        {
            try
            {
                int result = 0;

                // Check user already added or not.
                var existParticipant = participantDAO.SelectByUserIdSubjectId(participant.UserId, participant.SubjectId);
                if (existParticipant != null)
                {
                    return -1;
                }

                // Insert participant.
                participant.IsActivated = true;
                participant.JoinedTime = DateTime.Now;
                result = participantDAO.Insert(participant);

                return result;
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// Find list Participants of User
        /// </summary>
        /// <param name="userId">Id of User</param>
        /// <returns>List Participants</returns>

        public List<Participant> Find(int userId)
        {
            try
            {
                var participants = participantDAO.SelectByUserId(userId);
                return participants;
            }
            catch (Exception)
            {
                return null;
            }
        }

        /// <summary>
        /// Find participant.
        /// </summary>
        /// <param name="userId">User ID</param>
        /// <param name="subjectId">Subject ID</param>
        /// <returns>Participant object</returns>

        public Participant Find(int userId = 0, int subjectId = 0)
        {
            try
            {
                // Select participant by subject id and user id.
                var participant = participantDAO.SelectByUserIdSubjectId(userId, subjectId);

                return participant;
            }
            catch (Exception)
            {
                throw;
            }
        }

        public bool CheckParticipant(int userId, int subjectId)
        {
            var participant = participantDAO.SelectByUserIdSubjectId(userId, subjectId);

            if (participant == null)
            {
                return false;
            }

            if (!participant.IsActivated)
            {
                return false;
            }

            if (!participant.Subject.IsActivated)
            {
                return false;
            }

            if (!participant.User.IsActivated)
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// Deactivate participant.
        /// </summary>
        /// <param name="userIds">User ids tring</param>
        /// <param name="subjectId">Subject Id</param>
        /// <returns>Numnber of participant has been deactivated</returns>

        public int Deactivate(string userIds = "", int subjectId = 0)
        {
            try
            {
                StringHelper spliter = new StringHelper();

                // Split id string to int array.
                int[] userIdArr = spliter.Split(userIds);

                int result = 0;

                // Deactivate participants.
                foreach (var i in userIdArr)
                {
                    // Select participant by user id and subject id.
                    var participant = participantDAO.SelectByUserIdSubjectId(i, subjectId);

                    // Check participant is activated?
                    if (participant.IsActivated)
                    {
                        participant.IsActivated = false;
                        result += participantDAO.Update(participant);
                    }
                }

                return result;
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// Activate participant.
        /// </summary>
        /// <param name="userIds">User ids string</param>
        /// <param name="subjectId">Subject id</param>
        /// <returns>Number of participant has been activated</returns>

        public int Activate(string userIds = "", int subjectId = 0)
        {
            try
            {
                StringHelper spliter = new StringHelper();

                // Split id string to int array.
                int[] userIdArr = spliter.Split(userIds);

                int result = 0;

                // Deactivate participants.
                foreach (var i in userIdArr)
                {
                    // Select participant by user id and subject id.
                    var participant = participantDAO.SelectByUserIdSubjectId(i, subjectId);

                    // Check participant is inactivated?
                    if (!participant.IsActivated)
                    {
                        participant.IsActivated = true;
                        result += participantDAO.Update(participant);
                    }
                }

                return result;
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// Remove participant.
        /// </summary>
        /// <param name="ids"></param>
        /// <returns></returns>

        public int Remove(string userIds = "", int subjectId = 0)
        {
            try
            {
                StringHelper spliter = new StringHelper();

                // Split id string to int array.
                int[] userIdArr = spliter.Split(userIds);

                int result = 0;

                // Deactivate participants.
                foreach (var i in userIdArr)
                {
                    // Select participant by user id and subject id.
                    var participant = participantDAO.SelectByUserIdSubjectId(i, subjectId);
                    result += participantDAO.Delete(subjectId, i);
                }

                return result;
            }
            catch (Exception)
            {
                throw;
            }
        }
        /// <summary>
        /// import users to subject
        /// </summary>
        /// <param name="conection"></param>
        /// <param name="filepath"></param>
        /// <returns></returns>

        public Tuple<List<ParticipantErrorModel>, int, int, bool, bool> ParseFile(string conection, string filepath)
        {
            var listPart = new List<ParticipantErrorModel>();
            bool fatalError = false;
            int countSuccess = 0;
            int countFail = 0;
            var time = DateTime.Now.AddSeconds(60);
            bool timeout = false;

            try
            {
                // Fill the dataset
                var adapter = new OleDbDataAdapter("SELECT * FROM [Sheet1$]", conection);
                var ds = new DataSet();
                adapter.Fill(ds, "result");
                DataTable data = ds.Tables["result"];

                // check exist columns
                if (!data.Columns.Contains("Username"))
                {
                    fatalError = true;
                }
                else if (!data.Columns.Contains("Subject'sCode"))
                {
                    fatalError = true;
                }
                else
                {
                    for (int i = 0; i <= data.Rows.Count - 1; i++)
                    {
                        // check timeout
                        if (DateTime.Now > time)
                        {
                            timeout = true;
                            break;
                        }

                        // get data from excel
                        var partError = new ParticipantErrorModel();
                        partError.Username = data.Rows[i].Field<string>("Username");
                        partError.SubjectCode = data.Rows[i].Field<string>("Subject'sCode");

                        // check value in column
                        if (String.IsNullOrEmpty(partError.Username))
                        {
                            countFail++;
                            partError.Error = "Username is null or empty.";
                            listPart.Add(partError);
                        }
                        else if (String.IsNullOrEmpty(partError.SubjectCode))
                        {
                            countFail++;
                            partError.Error = "Subject's Code is null or empty.";
                            listPart.Add(partError);
                        }
                        else
                        {
                            // get userid and subject id
                            var user = userDAO.SelectByUsername(partError.Username);
                            var subject = subjectDAO.SelectByCode(partError.SubjectCode);
                            if (user == null)
                            {
                                countFail++;
                                partError.Error = "Username is not exist.";
                                listPart.Add(partError);
                            }
                            else if (subject == null)
                            {
                                countFail++;
                                partError.Error = "Subject is not exist.";
                                listPart.Add(partError);
                            }
                            else if (userDAO.SelectByUserIdSubjectId(user.UserId, subject.SubjectId) != null)
                            {
                                countFail++;
                                partError.Error = "The username was added to this subject.";
                                listPart.Add(partError);
                            }
                            else
                            {
                                // add value to object
                                var participant = new Participant();
                                participant.UserId = user.UserId;
                                participant.SubjectId = subject.SubjectId;
                                participant.IsActivated = true;
                                participant.JoinedTime = DateTime.Now;
                                int result = participantDAO.Insert(participant);
                                if (result == 0)
                                {
                                    countFail++;
                                    partError.Error = "Can't import this participant.";
                                    listPart.Add(partError);
                                }
                                else
                                {
                                    listPart.Add(partError);
                                    countSuccess++;
                                }
                            }

                        }
                    }
                }
                File.Delete(filepath);
                return new Tuple<List<ParticipantErrorModel>, int, int, bool, bool>(listPart, countSuccess, countFail, fatalError, timeout);
            }
            catch (Exception)
            {
                File.Delete(filepath);
                return new Tuple<List<ParticipantErrorModel>, int, int, bool, bool>(null, 0, 0, true, false);
            }

        }
    }
}